type op = Add | Sub | Mult | Div | Equal | Neq | Less | Leq | Greater | Geq
type nop = NAdd | NSub | NMult | NDiv (*the four normal operators*)

type bv = True | False
type bop = AND | OR 
type eop = BEqual | BNeq 
type rop = BLess | BLeq | BGreater | BGeq

type mop = MTimes | MDivide | MMod (*multiplicative expr ops*)
type aop = AAdd | ASub (*additve expr ops*)

type vop = VAdd | VSub | VMult | VDiv
type dt = StringType | DoubleType | IntType | BooleanType (*Data types in our language*)



(*
type value =
		Literal of int
	| Float of float
	| Boolean of bool
	| String of string
*)
(*
type arith_expr =
	| Literal of int
	| Float of float
	| Binop of arith_expr * op * arith_expr
*)



type vdecl = VDecl of dt * string * string  (*DataType, Name, Value*)
(*type vdecl = {
		vtype : string;
		(*vname : string;
		vvalue : string;*)
	}
*)


type arithexpr = (*key*)
	| ALiteral of int
	| AId of string
	| AFloat of float
(*
type multexpr =  
			MExpr of arithexpr
		|	Mop of multexpr * mop  *arithexpr 

type addexpr = 
	| AExpr of multexpr
	| Aop of addexpr * aop *multexpr
*)


(* original
type varexpr = (*key*)
	| VExpr of arithexpr
	| VStringLit of string
	| VBoolLit of bool 
*)
type varexpr = (*key*)
	| VLiteral of int
	| VId of string
	| VFloat of float
	| VStringLit of string
	| VBoolLit of bool
	| VBinop of varexpr *  vop * varexpr

type boolexpr =
	| RExpr of varexpr * rop * varexpr
	| EExpr of varexpr * eop * varexpr
	| BExpr of boolexpr * bop * boolexpr
	| BVal of bv 
	

(* backup - b4 chagin relxpre to boolxpr
type relexpr =
	| RExpr of varexpr * rop * varexpr
	| EExpr of varexpr * eop * varexpr
	| BXX of relexpr * bop * relexpr
	| BVal of bv 
	
type boolexpr = 
	| BExpr of relexpr * bop * relexpr		
*)	

type expr =
    Literal of int
	| Float of float
	| Boolean of bool
	| String of string
  | Id of string
	| Boolexpr of boolexpr 
  | Binop of varexpr * op * varexpr
  | Assign of string * expr
	| Call of string * expr list
  | Noexpr

type stmt =
    Block of stmt list
  | Expr of expr
	(*| Decl of dt * string * string*)
  | Return of expr
  | If of expr * stmt * stmt
  | For of expr * expr * expr * stmt
  | While of expr * stmt


(*type var_dec = Tuple of string * string*)


(*L System drawing function*)

type alphabet = 
	| Alphabet of string (*list maybe ?*)

type rules =
	| ERule of string * string (*Expansion rule : start_symbol -> Expansion.*) 
	| FRule of string * string * string (*Function rule : name | turtle function name | parameters. E.g f = turtle_move(100)*)
	 

type lfunc_decl = {
	name : string;
	formal : string list;
	alphabet : alphabet; 
	rules : rules list;	
}

type func =
	| CFunc of func_decl (*compute function*)
	| DFunc of lfunc_decl (*draw function*)
	

type func_decl = {
    fname : string;
    formals : string list;
    locals : vdecl list;
    body : stmt list;
  }

type program = func_decl list
(*type program = string list * func_decl list*)

let string_of_var_dec (a,b,c) = a ^ b ^ c 

let string_of_vop  = function
	| VAdd -> "+"
	| VSub -> "-"
	| VMult-> "*"
	| VDiv -> "/"

let string_of_arithexpr  = function
	| ALiteral(i) -> string_of_int i
	| AId(s) -> s
	| AFloat(f) -> string_of_float f

let rec string_of_varexpr = function
	| VLiteral(i) -> string_of_int i
	| VId(s) -> s
	| VFloat(f) -> string_of_float f
	| VStringLit(s) -> s
	| VBoolLit(b) -> string_of_bool b 
	| VBinop(v1,op,v2) -> string_of_varexpr v1 ^ " " ^ string_of_vop op ^ " " ^ string_of_varexpr v2  


let string_of_dt = function
	  StringType -> "string"
	| DoubleType -> "double"
	| IntType -> "int"
	| BooleanType -> "boolean"


let string_of_bop = function
	| AND -> "&&"
	| OR -> "||"

let string_of_rop = function 
	| BLess -> "<"
	| BLeq -> "<="
	| BGreater -> ">"
	| BGeq -> ">="

let string_of_eop = function
	| BEqual -> "=="
	| BNeq -> "!="

let string_of_bv = function 
	| True -> "true"
	| False -> "false"

let rec string_of_boolexpr = function
	| RExpr(v1,o,v2) -> string_of_varexpr v1 ^ " " ^ string_of_rop o ^ " " ^ string_of_varexpr v2
	| EExpr(v1,o,v2) -> string_of_varexpr v1 ^ " " ^ string_of_eop o ^ " "^  string_of_varexpr v2
	| BVal(v) -> string_of_bv v
	| BExpr(v1,o,v2) -> string_of_boolexpr v1 ^ " " ^ string_of_bop o ^ " "^  string_of_boolexpr v2


let rec string_of_expr = function
    Literal(l) -> string_of_int l
	| Boolean(b) -> string_of_bool b
	| Float(f) -> string_of_float f
	| String(s) -> s
  | Id(s) -> s
	| Boolexpr(v1) -> string_of_boolexpr v1 
  | Binop(e1, o, e2) ->
      string_of_varexpr e1 ^ " " ^
      (match o with
	Add -> "+" | Sub -> "-" | Mult -> "*" | Div -> "/"
      | Equal -> "==" | Neq -> "!="
      | Less -> "<" | Leq -> "<=" | Greater -> ">" | Geq -> ">=") ^ " " ^
      string_of_varexpr e2
  | Assign(v, e) -> v ^ " = " ^ string_of_expr e
  | Call(f, el) ->
      f ^ "(" ^ String.concat ", " (List.map string_of_expr el) ^ ")"
	| Noexpr -> ""

let rec string_of_stmt = function
    Block(stmts) ->
      "{\n" ^ String.concat "" (List.map string_of_stmt stmts) ^ "}\n"
  | Expr(expr) -> string_of_expr expr ^ ";\n";
  | Return(expr) -> "return " ^ string_of_expr expr ^ ";\n";
	(*| Decl(dtt, nm, v) -> string_of_dt dtt ^ " " ^ nm ^ " = " ^ v ^ ";\n" *)
  | If(e, s, Block([])) -> "if (" ^ string_of_expr e ^ ")\n" ^ string_of_stmt s
  | If(e, s1, s2) ->  "if (" ^ string_of_expr e ^ ")\n" ^
      string_of_stmt s1 ^ "else\n" ^ string_of_stmt s2
  | For(e1, e2, e3, s) ->
      "for (" ^ string_of_expr e1  ^ " ; " ^ string_of_expr e2 ^ " ; " ^
      string_of_expr e3  ^ ") " ^ string_of_stmt s
  | While(e, s) -> "while (" ^ string_of_expr e ^ ") " ^ string_of_stmt s


(*let string_of_vdecl id = "int " ^ id ^ ";\n"*)
let string_of_vdecl = function
	| VDecl(dtt, nm, v) ->  string_of_dt dtt ^ " " ^ nm ^ " = " ^ v ^ ";\n"
	


let string_of_fdecl fdecl =
	  fdecl.fname ^ "(" ^ String.concat ", " fdecl.formals ^ ") {\n" ^
	  String.concat "" (List.map string_of_vdecl fdecl.locals) ^
	  String.concat "" (List.map string_of_stmt fdecl.body) ^
	  "}\n"


let string_of_program (funcs) =
  
  (*String.concat "" (List.map string_of_vdecl vars) ^ "\n" ^*)
  String.concat "\n" (List.map string_of_fdecl funcs)	
  

